﻿/*********************************************************************************
**                                                                              **
**  Copyright (C) 2024-2025 LiLong                                              **
**  This file is part of OpenVisaApplication.                                   **
**                                                                              **
**  OpenVisaApplication is free software: you can redistribute it and/or modify **
**  it under the terms of the GNU General Public License as published by        **
**  the Free Software Foundation, either version 3 of the License, or           **
**  (at your option) any later version.                                         **
**                                                                              **
**  OpenVisaApplication is distributed in the hope that it will be useful,      **
**  but WITHOUT ANY WARRANTY; without even the implied warranty of              **
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               **
**  GNU General Public License for more details.                                **
**                                                                              **
**  You should have received a copy of the GNU General Public License           **
**  along with OpenVisaApplication. If not, see <https://www.gnu.org/licenses/>.**
**********************************************************************************/
#include "VersionCheck.h"

#include <QCustomUi/QCtmFile.h>
#include <nlohmann/json.hpp>

#include <QApplication>
#include <QDate>
#include <QDir>
#include <QFileInfo>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QStandardPaths>
#include <QThread>
#include <QVersionNumber>

struct VersionCheck::Impl
{
    QThread thread;
    QNetworkAccessManager* net;
    QString version;
    QString description;
    QString downloadUrl;
    const QString tempPath { QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/OpenVisaApplication/Update.temp" };
};

VersionCheck& VersionCheck::instance()
{
    static VersionCheck inst;
    return inst;
}

void VersionCheck::checkUpdate(bool manual)
{
    if (!manual)
    {
        auto ret = QCtmFile::load(m_impl->tempPath);
        if (ret)
        {
            QDate d = QDate::fromString(*ret, "yyyy-MM-dd");
            if (d == QDate::currentDate())
                return;
        }

        auto info = QFileInfo(m_impl->tempPath);
        if (auto dir = info.dir(); !dir.exists())
        {
            dir.mkpath(dir.path());
        }
        QCtmFile::save(m_impl->tempPath, QDate::currentDate().toString("yyyy-MM-dd").toUtf8());
    }
    QMetaObject::invokeMethod(
        this,
        [this, manual]
        {
            m_impl->net
                ->get(QNetworkRequest(
                    QUrl("https://gitee.com/api/v5/repos/llongww/OpenVisaApplication/releases?page=1&per_page=20&direction=desc")))
                ->setProperty("_manual", manual);
        });
}

const QString& VersionCheck::version() const { return m_impl->version; }

const QString& VersionCheck::description() const { return m_impl->description; }

const QString& VersionCheck::downloadUrl() const { return m_impl->downloadUrl; }

VersionCheck::VersionCheck() : m_impl(std::make_unique<Impl>())
{
    this->moveToThread(&m_impl->thread);
    m_impl->thread.start();
    QMetaObject::invokeMethod(this, &VersionCheck::init, Qt::BlockingQueuedConnection);
    connect(qApp, &QCoreApplication::aboutToQuit, this, [this] { delete m_impl->net; }, Qt::BlockingQueuedConnection);
}

VersionCheck::~VersionCheck()
{
    m_impl->thread.quit();
    m_impl->thread.wait();
}

void VersionCheck::init()
{
    m_impl->net = new QNetworkAccessManager(this);
    m_impl->net->setTransferTimeout(5000);
    connect(m_impl->net, &QNetworkAccessManager::finished, this, &VersionCheck::onNetworkFinished);
}

void VersionCheck::onNetworkFinished(QNetworkReply* reply)
{
    auto manual = reply->property("_manual").toBool();
    if (reply->error() != QNetworkReply::NoError)
    {
        emit errorOccurred(manual, u8"检查更新失败");
        return;
    }
    auto data = reply->readAll();
    onVersionCheck(manual, data);
}

void VersionCheck::onVersionCheck(bool manual, const QByteArray& data)
{
    try
    {
        auto json = nlohmann::json::parse(data.toStdString());
        if (!json.is_array())
            return;
        m_impl->version     = QString::fromStdString(json[0].value("tag_name", ""));
        m_impl->description = QString::fromStdString(json[0].value("body", ""));
        m_impl->downloadUrl = QString::fromStdString(json[0]["assets"][0].value("browser_download_url", ""));
        if (!m_impl->downloadUrl.endsWith(".exe"))
            m_impl->downloadUrl.clear();
        m_impl->version     = m_impl->version.right(m_impl->version.size() - 1);
        auto newVersion     = QVersionNumber::fromString(m_impl->version);
        auto currentVersion = QVersionNumber::fromString(OPENVISA_APPLICATION_VERSION);
        emit finished(manual, newVersion > currentVersion);
    }
    catch (const std::exception& e)
    {
        emit errorOccurred(manual, QString::fromLocal8Bit(e.what()));
    }
}
